首先,什么是WSGI?
WSGI, 全称为 Web Server Gateway Interface。
它不是什么框架,它是一个规范,但是作为一个规范,它实际上并没有官方标准。
这里要说明一下,PEP-0333并不是用来规范WSGI用得,而是PEP-0333提出应该有那么一个东西用来处理web服务器和应用服务器之间的关系,因此产生了WSGI。
其次,为什么PEP-0333觉得需要这个规范呢?
因为,就目前来看,Web服务一般按照如下方式部署:
先部署一个web服务器(Apache etc.)用于处理协议层的业务,比如,单台物理机多服务(多域名或者多端口)、负载均衡等。
然后部署一个应用服务器(Django etc.),它用于处理具体业务方面的事情,比如,滴滴打车的应用服务器就会处理打车订单CRUD,处理完成之后呢再返回给web服务器,web服务器收到响应之后再返回给客户端。
WSGI是如何工作的?
通过上文,我们可以了解到WSGI无非做了两件事:
让Web服务器知道如何调用Python应用程序并把从客户端来的请求拿过来。
让Python应用程序知道客户端的具体请求是什么,以及如何返回结果给Web服务器并帮助Python应用程序把计算后的结果返回给Web服务器。
也就是说,WSGI是连接Web服务器和应用服务器的桥梁。
目前实现的WSGI中,有两个角色,分别是Server/Gateway和application/framework。
当请求来临的时候,server
调用application
,然后application
把结果返回给server
。
那么,server是如何调用application的?
接下来,server
需要知道去哪能够找到application
。实现这一逻辑,需要在server
指定一个Python模块(具体在server的哪个位置保存这一路径,那就得根据具体server类型来选择了,如apache或nginx)该模块必须包含一个名称为application
的可调用对象,这个对象的形式如下:
在PEP-0333中指出了,一个WSGI的application角色,应该是一个可调用对象:
def application(environ, start_response)
其中,environ
是一个包含了关于这次HTTP请求信息的字典,start_response
是一个可调用对象,用于在application
中执行,用以在返回响应内容前设置响应的状态码和响应头,同时也意味着告诉server
,application
要开始返回http的body了。这两个就是server
调用application
时候需要传递的所有参数了。
那么,我们还需要再说一下,
environ
和start_response()
是需要在server
端的生成和定义的
有了这两个参数,application
就能知道用户请求的是什么资源,请求中带了什么数据,结果该如何返回给server
等等。其中,environ
包含了一些符合CGI规范的环境变量和WSGI规范新添加的变量,此外还可能有一些系统变量及Web服务器相关的环境变量。start_response
是一个可调用对象,它包含了一个表示HTTP响应状态的字符串和一个HTTP响应headers的列表以及一个用于出错返回的信息,具体参数包含及详情请点这里。
下面是一个完整的application demo,最终返回的body也可以是一个可迭代对象,这样server
也可以通过遍历这个对象来拼接成body。
def application(environ, start_response):
start_response("200 OK", [("Content-type", "text/plain")])
return ["Hello World!",]
看上去不错,那么我们该如何调用呢?
有几个服务器组件也能够运行的WSGI应用程序,但是对于简单的测试目的,我们可以使用包含在Python的标准库参考实现,就像下面那样:
if __name__ == '__main__':
from wsgiref.simple_server import make_server
server = make_server('localhost', 8080, application)
server.serve_forever()
好了,还记得我们刚才提到的最终返回的body也可以是一个可迭代对象吗?在python中,创建一个迭代器的简单办法就是使用一个生成器,比如,我们有一个用于静态文件的服务,我们可以写一个生成器,让它一次生成一个固定大小的文件块,这样我们就可以随时一次只存储一个文件块了,让我们来试试吧:
def send_file(file_path, size):
with open(file_path) as f:
block = f.read(BLOCK_SIZE)
while block:
yield block
block = f.read(BLOCK_SIZE)
对应的 WSGI application 部分如下:
size = os.path.getsize(file_path)
headers = [
("Content-type", mimetype),
("Content-length", str(size)),
]
start_response("200 OK", headers)
return send_file(file_path, size)
注意,send_file
就是上文中所指的可迭代对象。
WSGI中间件
看得出来,WSGI应用结构非常简洁,只需指定一个可调用的识别标记,这使得WSGI应用很容易实现调用其他框架,无非就是修改到来的请求或者修改发出的响应,当然也可以两者都有。那么,接下来看看demo:
class Filter(object):
def __init__(self, application):
self.application = application
def __call__(self, environ, start_response):
# Do something here to modify request
pass
# Call the wrapped application
app_iter = self.application(environ,
self._sr_callback(start_response))
# Do something to modify the response body
pass
# Return modified response
return app_iter
def _sr_callback(self, start_response):
def callback(status, headers, exc_info=None):
# Do something to modify the response status or headers
pass
# Call upstream start_response
start_response(status, headers, exc_info)
return callback
像这样应用通常被称为Middleware applications或Filter。 Filter可以被连接在一起,由此产生的链通常被称为pipeline。
最后呢,我想说明一下,WSGI是故意设计成最小的Web服务器实现轻松的应用,以便被更多的人采用。但是,几乎没有人真的喜欢直接操作environ
变量,也几乎没有人喜欢用start_response
这么诡异的逻辑,虽然WSGI提供的API易于实现,但这不代表它的语义让人满意。也正是因为这一原因,几乎每一个应用程序或者web框架都把environ
和start_response
封装成了语义更完善容错率更高的request和response对象。
Webob就是request和response对象的规范实现之一,它使得WSGI更容易和更满意地被大家使用。
你可以在这里找到Webob的官方文档,当然,下次有机会我也会简单的说明一下Webob到底有多方便。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。